import numpy as np
from sklearn.datasets import load_iris
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix, roc_curve, auc
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import warnings
warnings.filterwarnings('ignore')

class CreditScoreEvaluator:
    """信用评分卡评估器"""
    
    def __init__(self, model, X, y, feature_names):
        self.model = model
        self.X = X
        self.y = y
        self.feature_names = feature_names
        
    def calculate_iv(self, feature, target, bins=10):
        """计算单个特征的IV值"""
        if np.issubdtype(feature.dtype, np.number):
            # 连续变量进行分箱
            feature_cut = pd.qcut(feature, bins, duplicates='drop')
        else:
            # 离散变量直接使用原值
            feature_cut = feature
            
        woe_df = pd.DataFrame({
            'feature': feature_cut,
            'target': target
        })
        
        # 计算WOE和IV
        grouped = woe_df.groupby('feature')
        iv = 0
        
        total_pos = (target == 1).sum()
        total_neg = (target == 0).sum()
        
        for name, group in grouped:
            pos = (group['target'] == 1).sum()
            neg = (group['target'] == 0).sum()
            
            pos_rate = (pos + 0.5) / total_pos
            neg_rate = (neg + 0.5) / total_neg
            
            woe = np.log(neg_rate / pos_rate)
            iv += (neg_rate - pos_rate) * woe
            
        return iv
    
    def get_feature_importance(self):
        """计算所有特征的IV值"""
        iv_values = {}
        for i, feature_name in enumerate(self.feature_names):
            iv = self.calculate_iv(self.X[:, i], self.y)
            iv_values[feature_name] = iv
        return iv_values
    
    def plot_roc_curve(self, y_true, y_pred_proba):
        """绘制ROC曲线"""
        fpr, tpr, _ = roc_curve(y_true, y_pred_proba)
        roc_auc = auc(fpr, tpr)
        
        plt.figure(figsize=(10, 6))
        plt.plot(fpr, tpr, color='darkorange', lw=2, 
                label=f'ROC curve (AUC = {roc_auc:.2f})')
        plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')
        plt.xlim([0.0, 1.0])
        plt.ylim([0.0, 1.05])
        plt.xlabel('False Positive Rate')
        plt.ylabel('True Positive Rate')
        plt.title('Receiver Operating Characteristic (ROC)')
        plt.legend(loc="lower right")
        plt.grid(True)
        plt.show()
        
        return roc_auc
    
    def plot_iv_importance(self, iv_values):
        """绘制IV值重要性图"""
        plt.figure(figsize=(10, 6))
        iv_series = pd.Series(iv_values).sort_values(ascending=True)
        iv_series.plot(kind='barh')
        plt.title('Feature Importance (IV Values)')
        plt.xlabel('Information Value')
        plt.ylabel('Features')
        plt.grid(True)
        plt.tight_layout()
        plt.show()

class IrisClassifier:
    def __init__(self, max_iter=1000):
        """
        初始化鸢尾花分类器
        max_iter: 最大迭代次数，用于解决收敛警告
        """
        self.model = LogisticRegression(max_iter=max_iter)
        self.iris = load_iris()
        
    def prepare_data(self, test_size=0.2, random_state=42):
        """
        准备数据集
        """
        X = self.iris.data
        y = self.iris.target
        
        # 将多分类问题转换为二分类（为了演示信用评分卡）
        y = (y == 2).astype(int)  # 将virginica类作为正类
        
        # 划分训练集和测试集
        self.X_train, self.X_test, self.y_train, self.y_test = train_test_split(
            X, y, test_size=test_size, random_state=random_state)
        
        return self.X_train, self.X_test, self.y_train, self.y_test
    
    def train_model(self):
        """
        训练模型
        """
        self.model.fit(self.X_train, self.y_train)
        
    def evaluate_model(self):
        """
        评估模型
        """
        # 预测
        y_pred = self.model.predict(self.X_test)
        
        # 计算准确率
        accuracy = accuracy_score(self.y_test, y_pred)
        
        # 交叉验证
        cv_scores = cross_val_score(self.model, self.iris.data, self.iris.target, cv=5)
        
        return accuracy, cv_scores, y_pred
    
    def plot_confusion_matrix(self, y_pred):
        """
        绘制混淆矩阵
        """
        plt.figure(figsize=(10, 8))
        cm = confusion_matrix(self.y_test, y_pred)
        sns.heatmap(cm, annot=True, fmt='d', cmap='Blues')
        plt.title('Confusion Matrix')
        plt.xlabel('Predicted')
        plt.ylabel('True')
        plt.show()
        
    def plot_feature_importance(self):
        """
        绘制特征重要性
        """
        plt.figure(figsize=(10, 6))
        importance = np.abs(self.model.coef_).mean(axis=0)
        feature_names = self.iris.feature_names
        
        # 创建特征重要性条形图
        plt.bar(feature_names, importance)
        plt.title('Feature Importance')
        plt.xlabel('Features')
        plt.ylabel('Importance')
        plt.xticks(rotation=45)
        plt.tight_layout()
        plt.show()
    
    def plot_decision_regions(self):
        """
        绘制决策边界（使用前两个特征）
        """
        plt.figure(figsize=(10, 8))
        
        # 只使用前两个特征
        X = self.iris.data[:, [0, 1]]
        y = self.iris.target
        
        # 创建网格点
        x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
        y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
        xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.1),
                            np.arange(y_min, y_max, 0.1))
        
        # 训练模型并预测
        model = LogisticRegression(max_iter=1000)
        model.fit(X, y)
        Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
        Z = Z.reshape(xx.shape)
        
        # 绘制决策边界
        plt.contourf(xx, yy, Z, alpha=0.4)
        plt.scatter(X[:, 0], X[:, 1], c=y, alpha=0.8)
        plt.xlabel(self.iris.feature_names[0])
        plt.ylabel(self.iris.feature_names[1])
        plt.title('Decision Regions (first two features)')
        plt.show()

def main():
    # 创建分类器实例
    classifier = IrisClassifier()
    
    # 准备数据
    X_train, X_test, y_train, y_test = classifier.prepare_data()
    
    # 训练模型
    classifier.train_model()
    
    # 评估模型
    accuracy, cv_scores, y_pred = classifier.evaluate_model()
    
    # 创建信用评分评估器
    evaluator = CreditScoreEvaluator(
        classifier.model, 
        classifier.iris.data,
        (classifier.iris.target == 2).astype(int),  # 转换为二分类
        classifier.iris.feature_names
    )
    
    # 计算并展示IV值
    iv_values = evaluator.get_feature_importance()
    print("\n=== 特征IV值 ===")
    for feature, iv in sorted(iv_values.items(), key=lambda x: x[1], reverse=True):
        print(f"{feature}: {iv:.4f}")
    
    # 绘制ROC曲线
    print("\n=== ROC曲线评估 ===")
    y_pred_proba = classifier.model.predict_proba(X_test)[:, 1]
    auc_value = evaluator.plot_roc_curve(y_test, y_pred_proba)
    print(f"AUC值: {auc_value:.4f}")
    
    # 绘制IV值重要性图
    print("\n=== IV值重要性图 ===")
    evaluator.plot_iv_importance(iv_values)
    
    # 打印原有的评估结果
    print("\n=== 模型评估结果 ===")
    print(f"测试集准确率: {accuracy:.4f}")
    print(f"交叉验证准确率: {cv_scores.mean():.4f} (+/- {cv_scores.std() * 2:.4f})")
    
    print("\n=== 分类报告 ===")
    print(classification_report(y_test, y_pred))
    
    # 可视化结果
    print("\n=== 模型可视化 ===")
    classifier.plot_confusion_matrix(y_pred)
    classifier.plot_feature_importance()
    classifier.plot_decision_regions()

if __name__ == "__main__":
    main()
